﻿using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace ArtMath.PointCollection
{
    [Serializable]
    public class Curve : IList<PointD>
    {
        public enum FillType
        {
            ClosedCurve,
            Polygon,
            Path,
        }
        protected List<PointD> samples = new List<PointD>();
        public bool IsClosed;
        public PointF[] FSamples
        {
            get { return Utility.ToPointFs(samples); }
        }
        public double ConnectionLength
        {
            get
            {
                if (IsClosed)
                {
                    return samples[0].DistanceTo(samples[samples.Count - 1]);
                }
                else
                {
                    return double.PositiveInfinity;
                }
            }
        }

        public int Count
        {
            get { return samples.Count; }
        }

        public bool IsReadOnly
        {
            get
            {
                return ((IList<PointD>)samples).IsReadOnly;
            }
        }

        public PointD this[int index]
        {
            get { return samples[index]; }
            set { samples[index] = value; }
        }

        public void Paint(Graphics gra, Pen p, Brush bru = null, FillType type = FillType.ClosedCurve)
        {
            if (samples.Count == 0) return;
            PointF[] pts = FSamples;
            if (bru != null && pts.Length > 2)
            {
                switch (type)
                {
                    case FillType.Polygon:
                        gra.FillPolygon(bru, pts);
                        break;
                    case FillType.Path:
                        GraphicsPath path = new GraphicsPath();
                        path.AddCurve(pts);
                        path.CloseAllFigures();
                        gra.FillPath(bru, path);
                        break;
                }
            }
            if (samples.Count == 1)
            {
                if (p != null)
                {
                    gra.FillEllipse(new SolidBrush(p.Color), pts[0].X - p.Width / 2,
                        pts[0].Y - p.Width / 2, p.Width, p.Width);
                }
            }
            else
            {
                if (IsClosed && pts.Length > 2)
                {
                    if (bru != null && type == FillType.ClosedCurve)
                    {
                        gra.FillClosedCurve(bru, pts);
                    }
                    if (p != null)
                    {
                        gra.DrawClosedCurve(p, pts);
                    }
                }
                else
                {
                    if (p != null)
                    {
                        gra.DrawCurve(p, pts);
                    }
                }
            }
        }
        public PointD[] CurvesIntersection(Curve cur, Predicate<List<PointD>> judge = null)
        {
            return CurveIntersection.CurvesIntersection(samples, IsClosed, cur.samples, cur.IsClosed, judge);
        }
        public virtual void Add(PointD item)
        {
            samples.Add(item);
        }
        public void AddRange(IEnumerable<PointD> pts)
        {
            foreach (PointD item in pts)
            {
                Add(item);
            }
        }
        public virtual void RemoveAt(int index)
        {
            samples.RemoveAt(index);
        }
        public virtual void RemoveRange(int index, int count)
        {
            samples.RemoveRange(index, count);
        }
        public virtual void Clear()
        {
            samples.Clear();
        }
        public int GetIndex(PointD item)
        {
            for (int i = 0; i < samples.Count; i++)
            {
                int iNext;
                if (i == samples.Count - 1)
                {
                    if (IsClosed)
                    {
                        iNext = 0;
                    }
                    else
                    {
                        continue;
                    }
                }
                else
                {
                    iNext = i + 1;
                }
                if (item.OnLineSegment(samples[i], samples[iNext]))
                {
                    if (item == samples[iNext])
                    {
                        return iNext;
                    }
                    else
                    {
                        return i;
                    }
                }
            }
            return -1;
        }
        public bool Contains(PointD pt)
        {
            bool has = false;
            LineSegmentIterator((PointD pt1, PointD pt2) =>
            {
                if (pt.OnLineSegment(pt1, pt2))
                {
                    has = true;
                }
                return !has;
            });
            return has;
        }
        //public void GeneratePartialCircleSamples(PointD center, double r, double start, double sweep, double step)
        //{
        //    Samples.Clear();
        //    double stop = start + sweep;
        //    for (double theta = start; sweep >= 0 ? theta <= stop : theta >= stop; theta += step)
        //    {
        //        Samples.Add(r * theta.GetUnitVector() + center);
        //    }
        //    IsClosed = false;
        //}
        //public void GenerateCircleSamples(PointD center, double r, double step)
        //{
        //    GeneratePartialCircleSamples(center, r, 0, Angle.RadRound, step);
        //    IsClosed = true;
        //}
        //public void GeneratePartialEllipseSamples(PointD center, double rotate, double a, double b, double start, double sweep, double step)
        //{
        //    Samples.Clear();
        //    double stop = start + sweep;
        //    for (double theta = start; sweep >= 0 ? theta <= stop : theta >= stop; theta += step)
        //    {
        //        Samples.Add(new PointD(a * Math.Cos(theta), b * Math.Sin(theta)).Rotate(rotate) + center);
        //    }
        //    IsClosed = false;
        //}
        //public void GenerateEllipseSamples(PointD center, double rotate, double a, double b, double step)
        //{
        //    GeneratePartialEllipseSamples(center, rotate, a, b, 0, Angle.RadRound, step);
        //    IsClosed = true;
        //}
        //public void GeneratePartialHyperbolaSamples(bool left, PointD center, double rotate, double a, double b, double start, double sweep, double step)
        //{
        //    if (step <= 0) return;
        //    Samples.Clear();
        //    double stop = start + sweep;
        //    if (left)
        //    {
        //        for (double theta = Math.PI * 0.5; theta < Math.PI * 1.5; theta += step)
        //        {
        //            if (theta > stop) break;
        //            if (theta == Math.PI * 0.5 || theta < start) continue;
        //            Samples.Add(new PointD(a / Math.Cos(theta), b * Math.Tan(theta)).Rotate(rotate) + center);
        //        }
        //    }
        //    else
        //    {
        //        for (double theta = 0; theta < Math.PI * 0.5; theta += step)
        //        {
        //            if (theta > stop) break;
        //            if (theta < start) continue;
        //            Samples.Add(new PointD(a / Math.Cos(theta), b * Math.Tan(theta)).Rotate(rotate) + center);
        //        }
        //        for (double theta = Math.PI * 1.5; theta < Angle.RadRound; theta += step)
        //        {
        //            if (theta > stop) break;
        //            if (theta == Math.PI * 1.5 || theta < start) continue;
        //            Samples.Add(new PointD(a / Math.Cos(theta), b * Math.Tan(theta)).Rotate(rotate) + center);
        //        }
        //    }
        //    IsClosed = false;
        //}
        //public void GenerateHyperbolaSamples(bool left, PointD center, double rotate, double a, double b, double step)
        //{
        //    GeneratePartialHyperbolaSamples(left, center, rotate, a, b, 0, Angle.RadRound, step);
        //}
        //public void GeneratePartialParabolaSamples(PointD center, double rotate, double p, double start, double sweep, double step)
        //{
        //    if (step <= 0) return;
        //    Samples.Clear();
        //    double stop = start + sweep;
        //    for (double theta = 0; theta < Math.PI * 0.5; theta += step)
        //    {
        //        if (theta > stop) break;
        //        if (theta == 0 || theta < start) continue;
        //        double tan = Math.Tan(theta), tmp = 2 * p / tan;
        //        Samples.Add(new PointD(tmp / tan, tmp).Rotate(rotate) + center);
        //    }
        //    for (double theta = 1.5 * Math.PI; theta < Angle.RadRound; theta += step)
        //    {
        //        if (theta > stop) break;
        //        if (theta < start) continue;
        //        double tan = Math.Tan(theta), tmp = 2 * p / tan;
        //        Samples.Add(new PointD(tmp / tan, tmp).Rotate(rotate) + center);
        //    }
        //    IsClosed = false;
        //}
        //public void GenerateParabolaSamples(PointD center, double rotate, double p, double step)
        //{
        //    GeneratePartialParabolaSamples(center, rotate, p, 0, Angle.RadRound, step);
        //}
        public void LineSegmentIterator(Func<PointD, PointD, bool> act)
        {
            if (act == null) return;
            for (int i = 0; i < samples.Count; i++)
            {
                int iNext;
                if (i == samples.Count - 1)
                {
                    if (IsClosed)
                    {
                        iNext = 0;
                    }
                    else
                    {
                        continue;
                    }
                }
                else
                {
                    iNext = i + 1;
                }
                if (!act(samples[i], samples[iNext]))
                {
                    break;
                }
            }
        }

        public int IndexOf(PointD item)
        {
            return samples.IndexOf(item);
        }

        public virtual void Insert(int index, PointD item)
        {
            samples.Insert(index, item);
        }

        public void CopyTo(PointD[] array, int arrayIndex)
        {
            samples.CopyTo(array, arrayIndex);
        }

        public virtual bool Remove(PointD item)
        {
            return samples.Remove(item);
        }

        public IEnumerator<PointD> GetEnumerator()
        {
            return samples.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return samples.GetEnumerator();
        }
    }
}
